/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package k3tool.common.filter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Nodes;
import nu.xom.Serializer;
import nu.xom.XPathContext;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

/**
 *
 * @author Jeff Huang
 */
public class Html2XHtml {

    private static final Logger logger = Logger.getLogger(Html2XHtml.class.getName());

    /**
     * <ol>
     * <li> Translate HTML to XHTML using TagSoup.</li>
     * <li> Download Images when necessary. </li>
     * </ol>
     */
    public void filter(final File origHtmlFile, final URL baseUrl, final File outputXHtmlFile, String inputEncoding) {
        if (inputEncoding == null) {
            inputEncoding = "UTF-8";
        }

        if (!origHtmlFile.exists()) {
            throw new IllegalArgumentException(new FileNotFoundException(origHtmlFile.getAbsolutePath()));
        }
        try {
            XHtmlConstants.processXHtmlDocument(origHtmlFile, inputEncoding, new XHtmlConstants.XHtmlProcessor() {

                @Override
                public Object process(Document doc, XPathContext context) {
                    Nodes scriptNodes = doc.query("//html:script", context);
                    while (scriptNodes.size() > 0) {
                        scriptNodes.get(0).getParent().removeChild(scriptNodes.get(0));
                        scriptNodes = doc.query("//html:script", context);
                    }
                    handleImgSrc(doc, context, origHtmlFile, outputXHtmlFile, baseUrl);

                    FileOutputStream xhtmlFileOutputStream = null;

                    try {
                        xhtmlFileOutputStream = new FileOutputStream(outputXHtmlFile);
                        Serializer s = new Serializer(xhtmlFileOutputStream, "UTF-8");
                        s.setIndent(2);
                        s.write(doc);
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    } finally {
                        if (xhtmlFileOutputStream != null) {
                            try {
                                xhtmlFileOutputStream.close();
                            } catch (IOException ex) {
                                //do nothing
                            }
                        }

                    }

                    return null;
                }
            });
        } catch (Exception ex) {
            logger.log(Level.SEVERE, origHtmlFile.getAbsolutePath(), ex);
            throw new RuntimeException(ex);
        }

    }

    protected void handleImgSrc(Document doc, XPathContext context, File origHtmlFile, File outputXHtmlFile, URL baseUrl) throws RuntimeException {
        Nodes imageNodes = doc.query("//html:img", context);
        if (imageNodes.size() > 0) {
            for (int k = 0; k < imageNodes.size(); ++k) {
                Element el = (Element) imageNodes.get(k);
                String src = el.getAttributeValue("src");
                if (StringUtils.isEmpty(src)) {
                    continue;
                }
                if (!src.startsWith("/")) {
                    //could be a relative path or complete url
                    File localFile = new File(origHtmlFile.getAbsoluteFile().getParentFile(), src);
                    if (localFile.exists()) {
                        File targetFile = new File(outputXHtmlFile.getAbsoluteFile().getParent(), src);
                        targetFile.getAbsoluteFile().getParentFile().mkdirs();
                        try {
                            FileUtils.copyFile(localFile, targetFile);
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                        logger.info("Copy local file : " + localFile.getAbsolutePath() + " : " + src);
                        continue;
                    }
                }
                URL srcUrl = null;
                try {
                    if (baseUrl != null) {
                        srcUrl = new URL(baseUrl, src);
                    } else {
                        srcUrl = new URL(src);
                    }
                } catch (MalformedURLException ex) {
                    logger.log(Level.WARNING, src + " " + ex.getMessage());
                    continue;
                }
                String fileName = srcUrl.getFile();
                int pos = fileName.indexOf('?');
                if (pos != -1) {
                    fileName = fileName.substring(0, pos);
                }
                if (fileName.isEmpty()) {
                    continue;
                }
                pos = fileName.lastIndexOf('/');
                if (pos != -1) {
                    fileName = fileName.substring(pos + 1);
                }
                if (fileName.isEmpty()) {
                    continue;
                }
                //add prefix
                fileName = String.format("IMG-%08d-%s", new Random().nextInt(999999), fileName);
                InputStream is = null;
                FileOutputStream fos = null;
                File targetFile = new File(outputXHtmlFile.getAbsoluteFile().getParentFile(), fileName);
                if (targetFile.exists()) {
                    continue; //a simple way to avoid duplicated downloading. it is not always correct. but most of the time, we would allow the problem.
                }
                URLConnection conn = null;
                try {
                    conn = srcUrl.openConnection();
                    conn.setConnectTimeout(30 * 1000); //30 secondes
                    conn.connect();
                    is = conn.getInputStream();
                    fos = new FileOutputStream(targetFile);
                    IOUtils.copy(is, fos);
                } catch (IOException ex) {
                    logger.log(Level.WARNING, "url = " + srcUrl + " file = " + targetFile.getAbsolutePath(), ex);
                    continue;
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException ex) {
                            //do nothing
                        }
                    }
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException ex) {
                            //do nothing
                        }
                    }
                }
                el.getAttribute("src").setValue(fileName);
            }
        }
    }
}
